package com.xnyzc.lhy.resource.util.oss;

import com.alibaba.fastjson.JSONObject;
import com.qcloud.cos.COSClient;
import com.qcloud.cos.ClientConfig;
import com.qcloud.cos.auth.BasicCOSCredentials;
import com.qcloud.cos.auth.COSCredentials;
import com.qcloud.cos.exception.CosClientException;
import com.qcloud.cos.exception.CosServiceException;
import com.qcloud.cos.http.HttpMethodName;
import com.qcloud.cos.model.*;
import com.qcloud.cos.region.Region;
import com.tencent.cloud.CosStsClient;
import com.xnyzc.lhy.common.exception.OSSException;
import com.xnyzc.lhy.common.penum.EOSSErrorCode;
import com.xnyzc.lhy.common.util.DateUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.io.*;
import java.net.URL;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.TreeMap;

/**
 * <p>
 * 腾讯云cos对象存储服务
 * </p>
 *
 * @author haoyfa
 * @since 2019-10-19
 **/
@Slf4j
@Service
public class QCloudCOS implements IOSS {
    @Value("${global.qcloud.cos.secretid}")
    private String secretId;

    @Value("${global.qcloud.cos.secretkey}")
    private String secretKey;

    @Value("${global.qcloud.cos.regionstr}")
    private String regionStr;

    @Value("${global.qcloud.cos.bucketname}")
    private String bucketName;

    @Value("${global.qcloud.cos.validityTime}")
    private Integer validityTime;

    private final static String REDIO_DEFAULT_PATH = "sound/";

    /**
     * 腾讯云cos客户端
     */
    private COSClient cosClient = null;

    @Override
    public void Initializtion() {
        try {
            // 用户身份信息
            COSCredentials cred = new BasicCOSCredentials(secretId, secretKey);
            // 腾讯云的对象存储的区域
            Region region = new Region(regionStr);
            ClientConfig clientConfig = new ClientConfig(region);
            cosClient = new COSClient(cred, clientConfig);
        } catch (Exception exception) {
            log.error("腾讯云对象存储参数初始化错误");
            throw OSSException.create(EOSSErrorCode.initializtionError);
        }
    }

    /**
     * 上传文件
     *
     * @param key     对象键 格式：doc/picture.jpg
     * @param content 文件数据
     */
    @Override
    public void uploadFile(String key, byte[] content) {
        // 从输入流上传(需提前告知输入流的长度, 否则可能导致 oom)
        ByteArrayInputStream fileInputStream = new ByteArrayInputStream(content);

        ObjectMetadata objectMetadata = new ObjectMetadata();
        // 设置输入流长度为文件的字节数
        objectMetadata.setContentLength(content.length);

        try {
            cosClient.putObject(bucketName, key, fileInputStream, objectMetadata);
        } catch (CosServiceException serverException) {
            log.error("腾讯云对象存储服务器端错误:{}", serverException.getMessage());
            throw OSSException.create(EOSSErrorCode.serverError);
        } catch (CosClientException clientException) {
            log.error("腾讯云对象存储客户端错误");
            throw OSSException.create(EOSSErrorCode.clientError);
        }
    }

    /**
     * 获取文件访问路径
     *
     * @param key 对象键 格式：doc/picture.jpg
     * @return 文件URL
     */
    @Override
    public URL getFileUrl(String key) {
        URL url = null;

        try {
            GeneratePresignedUrlRequest req = new GeneratePresignedUrlRequest(bucketName, key, HttpMethodName.GET);

            // 设置URL过期时间时间（单位分钟）。
            Date expiration = DateUtil.dateAddMinutes(new Date(), validityTime);
            req.setExpiration(expiration);
            url = cosClient.generatePresignedUrl(req);
        } catch (CosServiceException serverException) {
            log.error("腾讯云对象存储服务器端错误");
            throw OSSException.create(EOSSErrorCode.serverError);
        } catch (CosClientException clientException) {
            log.error("腾讯云对象存储客户端错误");
            throw OSSException.create(EOSSErrorCode.clientError);
        }

        return url;
    }

    @Override
    public boolean getFileMessage(String key) {
        try {
            COSObject object = cosClient.getObject(bucketName, key);
        } catch (Exception e) {
            log.error("查询存储对象失败！错误为：{}", e.toString());
            return false;
        }
        return true;
    }

    /**
     * 关闭输入流
     */
    @Override
    public void shutdown() {
        if (cosClient != null) {
            cosClient.shutdown();
        }
    }

    /**
     * 获取动态秘钥
     */
    @Override
    public JSONObject getTemporary() {
        //方式一
        TreeMap<String, Object> config = new TreeMap<String, Object>();

        try {
            // 云 API 密钥 secretId
            config.put("secretId", secretId);
            // 云 API 密钥 secretKey
            config.put("secretKey", secretKey);

            // 临时密钥有效时长，单位是秒
            config.put("durationSeconds", 1800);

            // 换成你的 bucket
            config.put("bucket", bucketName);
            // 换成 bucket 所在地区
            config.put("region", regionStr);

            // 这里改成允许的路径前缀，可以根据自己网站的用户登录态判断允许上传的具体路径，
            // 例子： a.jpg 或者 a/* 或者 * (使用通配符*存在重大安全风险, 请谨慎评估使用)
            config.put("allowPrefix", "*");

            // 密钥的权限列表。简单上传和分片需要以下的权限，其他权限列表请看 https://cloud.tencent.com/document/product/436/31923
            String[] allowActions = new String[]{
                    // 简单上传
                    "name/cos:PutObject",
                    // 表单上传
                    "name/cos:PostObject",
                    // 分片上传： 初始化分片
                    "name/cos:InitiateMultipartUpload",
                    // 分片上传： 查询 bucket 中未完成分片上传的UploadId
                    "name/cos:ListMultipartUploads",
                    // 分片上传： 查询已上传的分片
                    "name/cos:ListParts",
                    // 分片上传： 上传分片块
                    "name/cos:UploadPart",
                    // 分片上传： 完成分片上传
                    "name/cos:CompleteMultipartUpload"
            };
            config.put("allowActions", allowActions);
            // 请求临时密钥信息
            //JSONObject credential = CosStsClient.getCredential(config);
            //return credential;
            return null;
        } catch (Exception e) {
            // 请求失败，抛出异常
            throw new IllegalArgumentException("no valid secret !");
        }
    }

    /**
     * 获取 uploadId
     *
     * @param savePath 保存路径
     * @return String
     */
    @Override
    public String getUploadId(String savePath) {
        savePath = REDIO_DEFAULT_PATH + savePath;
        InitiateMultipartUploadRequest request = new InitiateMultipartUploadRequest(bucketName, savePath);
        InitiateMultipartUploadResult initiateMultipartUploadResult = cosClient.initiateMultipartUpload(request);
        String uploadId = initiateMultipartUploadResult.getUploadId();
//        try {
//            uploadMultipleStart(uploadId, savePath);
//        } catch (IOException e) {
//            System.out.println(e);
//        }
        return uploadId;
    }

    /**
     * 开始分块上传
     */
    public void uploadMultipleStart(String uploadId, String key) throws IOException {
        // 上传分块, 最多10000个分块, 分块大小支持为1M - 5G。
        // 分块大小设置为4M。如果总计 n 个分块, 则 1 ~ n-1 的分块大小一致，最后一块小于等于前面的分块大小。
        List<PartETag> partETags = new ArrayList<PartETag>();
        int partNumber = 1;
        int partSize = 4 * 1024 * 1024;
        // partStream 代表 part 数据的输入流, 流长度为 partSize
        InputStream partStream = null;
        try {
            partStream = new FileInputStream("D:\\Lhy\\practice\\111.txt");
        } catch (FileNotFoundException e) {
            System.out.println(e);
        }
        // 最后一块的大小
        int partSizeLast = partStream.available();
        UploadPartRequest uploadRequest = new UploadPartRequest().withBucketName(bucketName).
                withUploadId(uploadId).withKey(key).withPartNumber(partNumber).
                withInputStream(partStream).withPartSize(partSizeLast);
        UploadPartResult uploadPartResult = cosClient.uploadPart(uploadRequest);
        // 获取 part 的 Etag
        String eTag = uploadPartResult.getETag();
        // partETags 记录所有已上传的 part 的 Etag 信息
        partETags.add(new PartETag(partNumber, eTag));
        // complete 完成分块上传.
        CompleteMultipartUploadRequest compRequest = new CompleteMultipartUploadRequest(bucketName, key, uploadId, partETags);
        CompleteMultipartUploadResult result = cosClient.completeMultipartUpload(compRequest);
        System.out.println(result);
    }
}
